package sk.stuba.fiit.pki.service.impl;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.security.InvalidKeyException;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
import java.security.SignatureException;
import java.security.UnrecoverableEntryException;
import java.security.cert.CRLException;
import java.security.cert.CertificateException;
import java.security.cert.X509CRL;
import java.security.cert.X509Certificate;
import java.util.Date;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataAccessException;
import org.springframework.stereotype.Service;

import sk.stuba.fiit.pki.core.CertificateManager;
import sk.stuba.fiit.pki.core.CrlManager;
import sk.stuba.fiit.pki.dao.CrlDao;
import sk.stuba.fiit.pki.entity.CRLEntity;
import sk.stuba.fiit.pki.service.CrlService;
import sk.stuba.fiit.pki.service.PkiService;
import sk.stuba.fiit.pki.ws.schema.beans.CrlRequest;
import sun.security.x509.X509CRLImpl;

@Service("crlService")
public class CrlServiceImpl implements CrlService {

	@Autowired()
	private CrlDao crlDao;

	public List<CRLEntity> findCrl() throws DataAccessException {
		return crlDao.findCRLs();
	}

	public void save(CRLEntity crlEntity) {
		crlDao.save(crlEntity);
	}

	public byte[] getActualCRL(CrlRequest request, BigInteger crlSerialNum, List<BigInteger> revocateCert) throws CRLException, InvalidKeyException, KeyStoreException, NoSuchAlgorithmException, CertificateException, UnrecoverableEntryException, NoSuchProviderException, SecurityException, SignatureException, IOException {
			CRLEntity lastCrl = findCrl().get(findCrl().size()-1);
			Date actualDate = new Date();
			if(actualDate.after(lastCrl.getNextUpdate()) || isNewRevocatedCert(revocateCert, lastCrl) ){
				InputStream in = new ByteArrayInputStream(lastCrl.getCrl());	
		    	X509CRL crl = new X509CRLImpl(in);
				byte[] actual = generateCRL(crlSerialNum, revocateCert, crl);
				lastCrl.setCrl(actual);
			}
			byte[] result = new byte[lastCrl.getCrl().length];
			result = lastCrl.getCrl();
			return result;
	}
	
	private boolean isNewRevocatedCert(List<BigInteger> revocateCert, CRLEntity lastCrl) throws CRLException{
		if(lastCrl == null)
			return true;
		
		X509CRL crl = new X509CRLImpl(lastCrl.getCrl()); 
		for (BigInteger bigInteger : revocateCert) {
			if(crl.getRevokedCertificate(bigInteger) == null)
				return true;
		}
		return false;
	}

	public  byte[] generateFirstCRL(BigInteger crlSerialNum,List<BigInteger> certSerialNum) throws InvalidKeyException, CRLException, NoSuchProviderException, SecurityException, SignatureException, IOException, KeyStoreException, NoSuchAlgorithmException, CertificateException, UnrecoverableEntryException {
		PrivateKey privateKey = CertificateManager.getInstance().loadPrivateKey(PkiService.STORAGE_TYPE, PkiService.PATH_FILE_NAME+PkiService.CA_ROOT_PATH, PkiService.CERT_PASSWORD);
		X509Certificate caCrlCert = CertificateManager.getInstance().loadCertificate(PkiService.PATH_FILE_NAME+PkiService.CA_ROOT_PATH, PkiService.CERT_PASSWORD, PkiService.STORAGE_TYPE);
		X509CRL crl = CrlManager.getInstance().generateCRL(crlSerialNum, certSerialNum, caCrlCert, privateKey);
		byte[] response = new byte[crl.getEncoded().length];
		
		CRLEntity crlEntity = new CRLEntity();
		crlEntity.setCrl(crl.getEncoded());
		crlEntity.setCrlserialNum(crlSerialNum.intValue());
		crlEntity.setNextUpdate(crl.getNextUpdate());
		crlEntity.setUpdateTime(crl.getThisUpdate());
		save(crlEntity);
		
		response =  crl.getEncoded();
		return response;
	}

	public  byte[] generateCRL(BigInteger crlSerialNum,List<BigInteger> certSerialNum,X509CRL crl) throws InvalidKeyException,
			KeyStoreException, NoSuchAlgorithmException, CertificateException,
			UnrecoverableEntryException, CRLException, NoSuchProviderException,
			SecurityException, SignatureException, IOException {
		
		PrivateKey privateKey = CertificateManager.getInstance().loadPrivateKey(PkiService.STORAGE_TYPE, 
				PkiService.PATH_FILE_NAME+PkiService.CA_ROOT_PATH, PkiService.CERT_PASSWORD);
		X509Certificate caCrlCert = CertificateManager.getInstance().loadCertificate(
				PkiService.PATH_FILE_NAME+PkiService.CA_ROOT_PATH, PkiService.CERT_PASSWORD, PkiService.STORAGE_TYPE);
		X509CRL actualCrl = CrlManager.getInstance().updateCRL(crl, crlSerialNum, certSerialNum, caCrlCert, privateKey);
		byte[] response = new byte[actualCrl.getEncoded().length];
		
		CRLEntity crlEntity = new CRLEntity();
		crlEntity.setCrl(actualCrl.getEncoded());
		crlEntity.setCrlserialNum(crlSerialNum.intValue());
		crlEntity.setNextUpdate(actualCrl.getNextUpdate());
		crlEntity.setUpdateTime(actualCrl.getThisUpdate());
		save(crlEntity);
		
		response = actualCrl.getEncoded();
		return response;
	}

	public void delete(CRLEntity crlEntity) {
		crlDao.delete(crlEntity);
	}

	public void deleteAllCertificates() {
		List<CRLEntity> crls = findCrl();
		for (CRLEntity crlEntity : crls) {
			delete(crlEntity);
		}
	}

}
